Pydantic for Rust. One derive. Full validation.
Why Rusdantic
Rusdantic unifies Serde serialization and validation into a single derive macro. No boilerplate, no runtime cost.
#[derive(Rusdantic)] generates Serialize, Deserialize, and Validate in a single attribute. One line of code replaces three crates.
Invalid structs never exist in memory. Validation runs during deserialization, so if you hold a value, it's guaranteed valid.
All validation logic is monomorphized at compile time. No trait objects, no dynamic dispatch, no runtime overhead.
Rusdantic vs Pydantic
Rusdantic goes beyond a port. These capabilities are unique to the Rust implementation.
Validation is fused into deserialization. Invalid data is rejected before a struct is ever constructed.
Pydantic: structs can exist in invalid stateValidation is monomorphized — the compiler generates specialized code for each type. No runtime overhead.
Pydantic: runtime validation overheadSecretStr uses timing-safe equality to prevent side-channel attacks on sensitive data.
Regex patterns are automatically wrapped in ^(?:...)$ for full-string matching. No accidental partial matches.
Built on Serde, not a separate serialization layer. Works with every Serde format out of the box.
Pydantic: custom serialization layerWorks on stable Rust with a minimum supported version of 1.70. No nightly compiler required.
Rusdantic: stable, predictable toolchainFeature Matrix
A complete comparison against the two most common Rust validation stacks.
| Feature | Rusdantic | serde + validator | serde + garde |
|---|---|---|---|
| Setup | 1 crate | 3+ crates | 2 crates |
| Validate on Deserialize | ✓ | ✗ | ✗ |
| Structured Error Paths | ✓ | partial | ✓ |
| Type Coercion | ✓ | ✗ | ✗ |
| Field Aliases | ✓ | ✗ | ✗ |
| Enum Validation | ✓ | limited | ✓ |
| Sanitizers (trim, lowercase) | ✓ | ✗ | ✗ |
| JSON Schema Generation | ✓ | ✗ | ✗ |
| PII Redaction | ✓ | ✗ | ✗ |
| Partial Validation | ✓ | ✗ | ✗ |
| Secret Types | ✓ | ✗ | ✗ |
| Settings Management | ✓ | ✗ | ✗ |
| Dump Options | ✓ | ✗ | ✗ |
| Constrained Types | ✓ | ✗ | partial |
| Nested Validation | ✓ | ✓ | ✓ |
Code Examples
Real examples showing how Rusdantic simplifies validation in Rust.
use rusdantic::prelude::*; #[derive(Rusdantic)] struct User { #[rusdantic(length(min = 1, max = 50))] name: String, #[rusdantic(email)] email: String, #[rusdantic(range(min = 0, max = 150))] age: u8, } fn main() { // Valid data - succeeds let json = r#"{"name":"Alice","email":"alice@example.com","age":30}"#; let user: User = serde_json::from_str(json).unwrap(); // Invalid data - fails at deserialization, never constructed let bad = r#"{"name":"","email":"not-email","age":200}"#; assert!(serde_json::from_str::<User>(bad).is_err()); }
use rusdantic::prelude::*; #[derive(Rusdantic)] #[rusdantic(coerce_mode = "lax")] struct Config { // "42" (string) will coerce to 42 (integer) port: u16, // "true", "1", "yes" all coerce to true debug: bool, // 3.14 (float) coerces to "3.14" (string) version: String, } fn main() { let json = r#"{"port":"8080","debug":"yes","version":2.1}"#; let cfg: Config = serde_json::from_str(json).unwrap(); assert_eq!(cfg.port, 8080); assert_eq!(cfg.debug, true); assert_eq!(cfg.version, "2.1"); }
use rusdantic::prelude::*; #[derive(Rusdantic)] struct Profile { #[rusdantic(trim, length(min = 1))] name: String, #[rusdantic(trim, lowercase)] username: String, #[rusdantic(redact)] ssn: String, } fn main() { let json = r#"{"name":" Alice ","username":" ALICE_99 ","ssn":"123-45-6789"}"#; let p: Profile = serde_json::from_str(json).unwrap(); assert_eq!(p.name, "Alice"); // trimmed assert_eq!(p.username, "alice_99"); // trimmed + lowercased println!("{}", p.ssn); // prints "**REDACTED**" }
use rusdantic::prelude::*; #[derive(Rusdantic)] struct Event { #[rusdantic(length(min = 1))] title: String, #[rusdantic(range(min = 1))] attendees: u32, #[rusdantic(email)] organizer: String, } fn main() { let schema = Event::json_schema(); println!("{}", serde_json::to_string_pretty(&schema).unwrap()); // Output: // { // "type": "object", // "properties": { // "title": { "type": "string", "minLength": 1 }, // "attendees": { "type": "integer", "minimum": 1 }, // "organizer": { "type": "string", "format": "email" } // }, // "required": ["title", "attendees", "organizer"] // } }
Full Feature Set
A batteries-included validation framework for Rust.
Crate Ecosystem
Use the facade crate for everything, or pick only what you need.
Facade crate. Re-exports everything. One dependency to rule them all.
Runtime validation logic, error types, and the Validate trait.
Proc macro that generates Serialize, Deserialize, and Validate impls.
Constrained types: SecretStr, EmailStr, PositiveInt, and more.
Settings management: env vars, .env files, JSON, TOML config loading.
Examples
Ten annotated examples covering every major feature.
Simple struct with min/max length, email, and range validators.
Lax and strict coercion modes for strings, numbers, and booleans.
Trim, lowercase, uppercase, and strip sanitizers in action.
Redact, mask, and hash modes for protecting sensitive data.
Nested structs with recursive validation and error paths.
Validating enum variants with per-variant constraints.
Generate JSON Schema from any Rusdantic model.
Load config from env vars, .env files, and JSON/TOML.
Write custom validation functions with full error context.
Partial models for PATCH endpoints and incremental updates.